<template>
	<!-- 索引列表 -->
	<view class="root" :style="{ height: height || contentHeight }">
		<!-- scroll-view 开启动画如果切换元素过快会出现不准确  -->
		<scroll-view
			:scroll-top="scrollTop"
			class="scrollView"
			:scroll-into-view="currentScrollIntoView"
			@scroll="scroll"
			scroll-y="true"
			id="trigger"
		>
			<view v-for="(item, index) in indexList" :key="index">
				<trigger-indexList-item ref="listItem" :item="item" :id="`id${item.data[0].index}`">
					<template #lineBody>
						<view
							class="lineBody"
							v-for="(child, childIndx) in item.data"
							:key="childIndx"
						>
							<slot name="childindex" :child="child">{{ child.name }}</slot>
						</view>
					</template>
				</trigger-indexList-item>
			</view>
		</scroll-view>

		<view class="indexBox" @touchstart="touchstart" @touchend="touchend" @touchmove="touchmove">
			<view
				class="index"
				ref="indexBox"
				:style="currentIndex == index ? `background:${indexActiveColor};color:white` : ''"
				v-for="(item, index) in indexList"
				@click="onclick(item, index)"
				:key="index"
			>
				{{ item.name }}
			</view>
			<view class="moveBox" :class="isTouch ? 'show' : ''" :style="style">
				<view class="text" v-if="currentIndex != null">
					{{ indexList[currentIndex].name }}
				</view>
			</view>
		</view>
	</view>
</template>

<script>
import { convertPinyin, debounce } from './components/trigger-indexList/utils/index.js';

import props from './props';
import triggerIndexListItem from './components/trigger-indexList_item/trigger-indexList_item';
export default {
	props,
	components: {
		triggerIndexListItem
	},
	data() {
		return {
			children: [],
			contentHeight: uni.getSystemInfoSync().windowHeight + 'px',
			currentIndex: null, // 当前索引
			currentScrollIntoView: 'id3', // 位移元素(由于跳转元素不在一个页面中，再加上uni不能动态slot) 故用scrollTop替代
			scrollTop: 0,
			indexList: [], // 数据
			indexBoxheightList: [],
			indexBoxInfo: {
				height: 0, // 高度
				childHeight: 0, // 单个子节点高度,
				top: 0, //距离顶部的高度 ， 手指移入实践是从最顶部开始计算的
				moveBoxHeight: 0 // 移动盒子的高度
			},
			isTouch: false // 是否为手指滑动期间，滑动期间不处理滚动事件
		};
	},

	methods: {
		getDomInfo(selector, all) {
			// // 可一次查多个元素listItem Dom信息
			// this.indexBoxheightMap = {};
			return new Promise(resolve => {
				//利用map循环把每个我们需要的Dom信息放到一个变量里存放住
				const res = this.$refs.listItem.map((item, index) => {
					const itemObj = {
						index, //滑动选项索引,控制右侧导航启动点就是他
						height: item.height, // 关键点.我们需要在滑动时 判断当前滑动点
						top: item.top // 判断距离是否在此选项内
					};
					// this.indexBoxheightMap[Math.floor(item.top)] = itemObj
					return itemObj;
				});
				resolve(res);
			});
		},
		getIndexBoxDomInfo(selector, all) {
			// 可一次查多个元素indexBox Dom信息
			const map = {};
			return new Promise(resolve => {
				this.$refs.indexBox.map((item, index) => {
					map[item.$el.offsetTop] = {
						index,
						innerText: item.$el.innerText,
						offsetTop: item.$el.offsetTop
					};
				});
				resolve(map);
			});
		},
		/**
		 * 滑动右侧索引
		 * @param {Object} e
		 */
		onclick(item, index) {
			this.isTouch = true;
			this.currentIndex = index;
			this.currentScrollIntoView = 'id' + item.name;
		},
		touchstart(e) {
			this.touchstartClientY = e.touches[0].clientY;
			this.isTouch = true;
		},
		/**
		 * 右侧导航滑动事件
		 */
		touchmove(e) {
			this.rightTouchmove(e, e.touches[0].clientY - this.touchstartClientY);
		},
		/**
		 * 右侧导航滑动
		 * @param {Object} e 滑动自带参数对象
		 * @param {string} distance 滑动距离
		 */
		rightTouchmove(e, distance) {
			if (this.rightIndexBoxMap[Math.floor(distance)]) {
				this.currentScrollIntoView =
					'id' + this.rightIndexBoxMap[Math.floor(distance)].innerText;
				this.currentIndex = this.rightIndexBoxMap[Math.floor(distance)].index;
			}
		},
		/**
		 * @param {Object} e 滑动索引列表中每一个对像
		 */
		scroll(e) {
			console.log(e);
			console.log(Math.floor(e.detail.scrollTop));
			this.stickyScroll(e);
		},
		/**
		 * @param {Object} e 滑动索引列表中每一个对像
		 */
		stickyScroll(e) {
			// if (this.indexBoxheightMap[Math.floor(e.detail.scrollTop)]) {
			// 	this.$nextTick(() => {
			// 		// 在范围内就找到所对应的Dom 哪个所在的滑动选项吸顶了
			// 		// 然后赋值  this.currentIndex //改值直接控制右侧导航索引
			// 		this.currentIndex = this.indexBoxheightMap[
			// 			Math.floor(e.detail.scrollTop)
			// 		].index;
			// 	});
			// }
			/** 屏幕滑动距离 indexBoxheightList已存放好的滑动item **/
			this.indexBoxheightList.forEach((item, index) => {
				//Math.abs 负数转正数
				//e.detail.scrollTop 当前滑动点的top值 属于scroll事件自带参数
				if (Math.abs(e.detail.scrollTop - item.top) < 10) {
					this.$nextTick(() => {
						// 在范围内就找到所对应的Dom 哪个所在的滑动选项吸顶了
						// 然后赋值  this.currentIndex //改值直接控制右侧导航索引
						this.currentIndex = index;
					});
				}
			});
		},
		touchend() {
			// 防止动画消失太快
			clearInterval(this.time);
			this.time = setTimeout(() => {
				this.isTouch = false;
			}, 300);
		},
		// 节流:触发函数事件后，短时间间隔内无法连续调用，只有上一次函数执行后，过了规定的时间间隔，才能进行下一次的函数调用。
		throttle(fn, delay) {
			let timer = true;
			return function(args) {
				let that = this;
				let _args = arguments;
				if (!timer) {
					return false;
				}
				timer = false;
				setTimeout(() => {
					fn.apply(that, _args);
					timer = true;
				}, delay);
			};
		},

		async initData() {
			let generateArray = () => {
				let array = JSON.parse(JSON.stringify(this.list));
				let arrGenerate = [];
				if (this.indexOrderList.length) {
					// 在咩有自定义索引配置是使用自应用
					for (let i = 0; i <= this.indexOrderList.length; i++) {
						let obj = {
							name: i,
							data: []
						};
						/** 按照顺序向数据源push 数据 **/
						array.map((item, index) => {
							if (item.index == i) obj.data.push(item);
						});
						// console.log(obj);
						if (this.showEmptyClass || obj.data.length > 0) {
							arrGenerate.push(obj);
							// arrGenerate[i]=obj;
						}
					}
				} else {
					// 在咩有自定义索引配置是使用默认 顺序排序 A-Z
					array.map((item, index) => {
						item.index = convertPinyin(item.name, 1);
					});
					for (var i = 0; i < 26; i++) {
						var letter = String.fromCharCode(65 + i);
						let obj = {
							name: letter,
							data: []
						};
						array.map((item, index) => {
							if (item.index == letter || item.index == letter.toLowerCase())
								obj.data.push(item);
						});
						if (this.showEmptyClass || obj.data.length > 0) {
							arrGenerate.push(obj);
						}
					}
				}

				return arrGenerate;
			};
			this.indexList = generateArray();

			this.$nextTick(() => {
				setTimeout(async () => {
					this.indexBoxheightList = await this.getDomInfo();
					this.rightIndexBoxMap = await this.getIndexBoxDomInfo();
				});
			});
		}
	},

	async mounted() {
		this.initData();
	},

	computed: {
		/** 右侧滑动条滑动时 左侧显示框定位 **/
		moveBoxStyles() {
			let { childHeight, top, moveBoxHeight } = this.indexBoxInfo;

			let calcTop = childHeight * (this.currentIndex + 1) - moveBoxHeight / 2 + top;

			// #ifdef H5
			if (!this.h5NoTopBar) {
				calcTop += 44;
			}
			// #endif
			let style = {
				top: calcTop + 'px',
				...this.moveBoxStyle
			};
			return style;
		},
		/**锚点定位 **/
		AbortControllertion() {
			return;
		},
		style() {
			return [this.moveBoxStyle];
		}
	},
	options: {
		styleIsolation: 'shared',
		virtualHost: true
	}
};
</script>

<style lang="scss" scoped>
.root {
	position: relative;

	.scrollView {
		height: 92vh;
		// background: seagreen;
		overflow: auto;
		display: flex;
		flex-direction: column;
		position: relative;
	}

	.indexBox {
		z-index: 999;
		top: 50%;
		transform: translate(0, -50%);
		display: flex;
		position: fixed;
		flex-direction: column;
		right: 0;
		gap: 5rpx;

		.index {
			width: 35rpx;
			height: 35rpx;
			padding: 10rpx 0;
			color: white;
			font-size: 26rpx;
			font-weight: bold;
			color: #606266;
			display: flex;
			align-items: center;
			border-radius: 20rpx;
			justify-content: center;
		}
	}

	.moveBox {
		z-index: 99;
		width: 100rpx;
		height: 100rpx;
		background: #14c893;
		position: fixed;
		right: 60rpx;
		top: 50%;
		transform: rotate(-45deg) translateY(-50%);
		border-radius: 100px 100px 0 100px;
		opacity: 0;
		display: flex;
		align-items: center;
		justify-content: center;

		.text {
			transform: rotate(45deg);
			color: white;
			font-size: 30rpx;
		}
	}

	.noShow {
	}

	.show {
		opacity: 1;
	}
}
.lineBody {
	display: flex;
	align-items: center;
	padding: 20rpx 30rpx;
	box-sizing: border-box;
	border-bottom: 1px solid rgba(164, 164, 164, 0.2);
}
</style>
